home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume2 / cdiff-v2 < prev    next >
Encoding:
Internet Message Format  |  1991-08-07  |  57.7 KB

  1. From: mike2@lcuxa.UUCP
  2. Newsgroups: comp.sources.misc
  3. Subject: v02i008: PD "diff -c": Turbo C compatibility and better manual
  4. Message-ID: <7082@ncoast.UUCP>
  5. Date: 15 Jan 88 01:11:39 GMT
  6. Approved: allbery@ncoast.UUCP
  7.  
  8. Comp.Sources.Misc: Volume 2, Issue 8
  9. Submitted-By: <mike2@lcuxa.UUCP>
  10. Archive-Name: cdiff-v2
  11.  
  12. After receiving Bob Larson's sources for the PD context diff program,
  13. I decided to accept his challenge to rewrite the documentation.  In
  14. the process, I also ported it to TURBOC version 1.5.  It probably will
  15. also compile in TURBOC 1.0, but since getting the update I dispensed
  16. with the previous version and did not try it.  
  17.  
  18. The code has been reorganized to strip it of the documentation that
  19. was built into it; that has been moved to the file cdiff.mem.  Thus,
  20. the following shar file includes cdiff.c, cdiff.1 (man source), cdiff.mem
  21. (the previously built-in documentation), cdiff.doc (cdiff.1 passed
  22. through nroff -man for those who do not have nroff available), the
  23. original README, and a new TC-READ.ME.  Follow the notes in TC-READ.ME
  24. or it will run even slower!  
  25.  
  26. Of course, no warranties whatsoever go with this.  I merely hacked the
  27. code minimally.  I didn't write it.
  28. ------------------------------CUT HERE--------------------------------
  29. #! /bin/sh
  30. # This is a shell archive, meaning:
  31. # 1.  Remove everything above the #! /bin/sh line.
  32. # 2.  Save the resulting text in a file
  33. # 3.  Execute the file with /bin/sh (not csh) to create the files:
  34. #
  35. #        README
  36. #        TC-READ.ME
  37. #        cdiff.1
  38. #        cdiff.c
  39. #        cdiff.doc
  40. #        cdiff.mem
  41. #
  42. # Created on 'date'
  43. #
  44. if test -f 'README'
  45. then
  46.     echo shar: will not over-write existing file "'README'"
  47. else
  48. echo extracting "'README'"
  49. sed 's/^X//' >README <<'SHAR_EOF'
  50. XHere's a public domain diff with the -b and -c options.  (4.2bsd style
  51. Xcontex diffs.)  I wasn't aware that these wern't present in all unix
  52. Xversions of diff, so I didn't think posting it was a priority.  It's
  53. Xlarge, slow, and many of the comments are no longer true, but it does
  54. Xwork.  (except when it runs out of memory)  The one case I know of
  55. Xwhere its output is incompatable with patch does seem to be pretty
  56. Xrare.  No makefile is included, the 4.2bsd diff is better on the unix
  57. Xsystem I use.  If you don't know how to compile and load a single C
  58. Xprogram, this probably isn't the tool for you anyway.  I'd be grateful
  59. Xto anyone who cleans this up and documents it properly.  It does
  60. Xappear to have been separate files at some point, I'm presenting it in
  61. Xa form similar to how I got it: mail headers and outdated documentation
  62. Xin comments and all.  I just banged on it enough to get it doing what
  63. XI wanted.
  64. SHAR_EOF
  65. if test 910 -ne "`wc -c < 'README'`"
  66. then
  67.     echo shar: error transmitting "'README'" '(should have been 910 characters)'
  68. fi
  69. fi
  70. if test -f 'TC-READ.ME'
  71. then
  72.     echo shar: will not over-write existing file "'TC-READ.ME'"
  73. else
  74. echo extracting "'TC-READ.ME'"
  75. sed 's/^X//' >TC-READ.ME <<'SHAR_EOF'
  76. X
  77. X1.  Make certain that the first line is a #define TURBO 1 line.
  78. X2.  Compile in the Large model.
  79. X3.  Set the optimizations:
  80. X    a) to optimize for speed, not size
  81. X    b) to use register variables
  82. X    c) to invoke register optimization
  83. X    c) to invoke jump optimization
  84. X4.  Even with the foregoing, this program can run slowly on a large file
  85. X    (as much as 60 seconds to compare two 40K files in a 4.77 MHz PC!)
  86. X    Be patient.
  87. X
  88. SHAR_EOF
  89. if test 419 -ne "`wc -c < 'TC-READ.ME'`"
  90. then
  91.     echo shar: error transmitting "'TC-READ.ME'" '(should have been 419 characters)'
  92. fi
  93. fi
  94. if test -f 'cdiff.1'
  95. then
  96.     echo shar: will not over-write existing file "'cdiff.1'"
  97. else
  98. echo extracting "'cdiff.1'"
  99. sed 's/^X//' >cdiff.1 <<'SHAR_EOF'
  100. X.TH CDIFF 1
  101. X.SH NAME
  102. Xcdiff \- Public Domain cdiff (context diff) program
  103. X.SH SYNOPSIS
  104. X.B diff
  105. X[
  106. X.B \-b \-c \-i \-e
  107. X] file1 file2
  108. X.SH DESCRIPTION
  109. X.I Diff\^
  110. Xcompares two files, showing what must be changed to make them
  111. Xidentical. Either file1 or file2 (but not both) may refer to
  112. Xdirectories. If that is the case, a file in the directory whose
  113. Xname is the same as the other file argument will be used. The
  114. Xstandard input may be used for one of the files by replacing the
  115. Xargument by "-". Except for the standard input, both files must
  116. Xbe on disk devices.
  117. X.SH OPTIONS
  118. X.HP
  119. X.B \-b  
  120. XRemove trailing whitespace (blanks and tabs)
  121. Xand compress all other strings of whitespace to a single blank.
  122. X.HP
  123. X.B \-c  
  124. XPrint some context -- matching lines before
  125. Xand after the non-match section.  Mark non-matched sections
  126. Xwith "|".
  127. X.HP
  128. X.B \-i  
  129. XIgnore lower/upper case distinctions.
  130. X.HP
  131. X.B \-e  
  132. XOutput is in an "editor script" format which
  133. Xis compatible with the Unix 'ed' editor.
  134. X.PP
  135. XAll information needed to compare the files is maintained in main
  136. Xmemory. This means that very large files (or fairly large files
  137. Xwith many differences) will cause the program to abort with an
  138. X"out of space" message. Main memory requirements (in words) are
  139. Xapproximately:
  140. X.br
  141. X
  142. X.ce
  143. X2 * (length of file1 + length of file2)
  144. X.br
  145. X.ce
  146. X+ 3 * (number of changes)
  147. X.br
  148. X.PP
  149. X(Where "length" is the number of lines of data in each file.)
  150. X.PP
  151. XThe algorithm reads each file twice, once to build hash tables
  152. Xand once to check for fortuitous matches (two lines that are in
  153. Xfact different, but which have the same hash value). CPU time
  154. Xrequirements include sorting the hash tables and randomly
  155. Xsearching memory tables for equivalence classes. For example, on
  156. Xa time-shared VAX-11/780, comparing two 1000 line files required
  157. Xabout 30 seconds (elapsed clock time) and about 10,000 bytes of
  158. Xworking storage. About 90 per-cent of the time was taken up by
  159. Xfile I/O.
  160. X.SH DIAGNOSTICS
  161. X.HP
  162. XWarning, bad option 'x'
  163. X.br
  164. XThe option is ignored.
  165. X.HP
  166. XUsage ...
  167. X.br
  168. XTwo input files were not specified.
  169. X.HP
  170. XCan't open input file "filename".
  171. X.br
  172. XCan't continue.
  173. X.HP
  174. XOut of space
  175. X.br
  176. XThe program ran out of memory while comparing the two files.
  177. X.HP
  178. XCan't read line nnn at xxx in file[A/B]
  179. X.br
  180. XThis indicates an I/O error when seeking to the specific line.
  181. XIt should not happen.
  182. X.HP
  183. XSpurious match, output is not optimal.
  184. X.br
  185. XTwo lines that were different yielded the same hash value.  This is
  186. Xharmless except that the difference output is not the minimum set of
  187. Xdifferences between the two files.  For example, instead of the output:
  188. X.ce
  189. Xlines 1 to 5 were changed to ...
  190. X.br
  191. Xthe program will print
  192. X.ce
  193. Xlines 1 to 3 were changed to ...
  194. X.br
  195. X.ce
  196. Xlines 4 to 5 were changed to ...
  197. X.br
  198. X.HP
  199. XThe program uses a CRC16 hash code.
  200. X.br
  201. XThe likelihood of this error is
  202. Xquite small.
  203. X.SH AUTHOR
  204. XThe diff algorithm was developed by J. W. Hunt and M. D. McIlroy,
  205. Xusing a central algorithm defined by H. S. Stone.
  206. XIt was published in:
  207. X.in +5
  208. X.nf
  209. XHunt, J. W., and McIlroy, M. D.,
  210. XAn Algorithm for Differential File Comparison,
  211. XComputing Science Technical Report #41,
  212. XBell Laboratories, Murray Hill, NJ  07974
  213. X.fi
  214. X.in -5
  215. X.SH BUGS
  216. XOn RSX and DECUS C on VMS systems, diff may fail if the both
  217. Xfiles are not "variable-length, implied carriage control" format.
  218. XThe scopy program can be used to convert files to this format if
  219. Xproblems arise.
  220. X.PP
  221. XWhen compiled under VAX C, diff handles STREAM_LF files properly
  222. X(in addition to the canonical variable-length implied carriage
  223. Xcontrol files). Other variations should work, but have not been
  224. Xtested.
  225. X.PP
  226. XWhen compiled under VAX C, diff is quite slow for unknown reasons
  227. Xwhich ought to be investigated. On the other hand, it has access
  228. Xto effectively unlimited memory.
  229. X.PP
  230. XOutput in a form suitable for ed - the -e option - seems rather
  231. Xpointless; the analogue on DEC systems is SLP (SUMSLP on VMS). It
  232. Xwould be simple to provide SLP-compatible output. The question
  233. Xis, why bother - since the various DEC file comparison utilities
  234. Xalready produce it.
  235. SHAR_EOF
  236. if test 4003 -ne "`wc -c < 'cdiff.1'`"
  237. then
  238.     echo shar: error transmitting "'cdiff.1'" '(should have been 4003 characters)'
  239. fi
  240. fi
  241. if test -f 'cdiff.c'
  242. then
  243.     echo shar: will not over-write existing file "'cdiff.c'"
  244. else
  245. echo extracting "'cdiff.c'"
  246. sed 's/^X//' >cdiff.c <<'SHAR_EOF'
  247. X/* Change the following as appropriate */
  248. X#undef vms
  249. X#undef unix
  250. X#undef pdp11
  251. X#undef OSK
  252. X#undef DEBUG
  253. X#define TURBO 1
  254. X
  255. X#ifdef TURBO
  256. X#include <alloc.h>
  257. X#include <errno.h>
  258. X#include <process.h>
  259. X#include <stdio.h>
  260. X#include <stdlib.h>
  261. X#include <string.h>
  262. X
  263. X#else
  264. X#include <stdio.h>
  265. X#include <ctype.h>
  266. X#endif
  267. X
  268. X#ifdef vms
  269. X#include      <ssdef.h>
  270. X#include      <stsdef.h>
  271. X#define  IO_SUCCESS  (SS$_NORMAL | STS$M_INHIB_MSG)
  272. X#define  IO_ERROR  SS$_ABORT
  273. X#endif
  274. X/*
  275. X * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
  276. X */
  277. X#ifndef  IO_SUCCESS
  278. X#define  IO_SUCCESS  0
  279. X#endif
  280. X#ifndef  IO_ERROR
  281. X#define  IO_ERROR  1
  282. X#endif
  283. X
  284. X#define  EOS      0
  285. X#ifdef unix
  286. Xchar temfile[L_tmpnam];
  287. Xchar *tmpnam();
  288. X#define  TEMPFILE  (temfile[0]? temfile: (tmpnam(temfile), temfile))
  289. X#else
  290. X#define  TEMPFILE  "diff.tmp"
  291. X#endif
  292. X#define  TRUE      1
  293. X#define  FALSE      0
  294. X
  295. X#ifdef  pdp11
  296. X#define  short  int
  297. X#endif
  298. X
  299. Xtypedef struct candidate {
  300. X  int  b;        /* Line in fileB      */
  301. X  int  a;        /* Line in fileA      */
  302. X  int  link;        /* Previous candidate      */
  303. X} CANDIDATE;
  304. X
  305. Xtypedef struct line {
  306. X  unsigned short  hash;      /* Hash value etc.      */
  307. X  short      serial;      /* Line number        */
  308. X} LINE;
  309. X
  310. XLINE  *file[2];        /* Hash/line for total file  */
  311. X#define  fileA  file[0]
  312. X#define  fileB  file[1]
  313. X
  314. XLINE  *sfile[2];        /* Hash/line after prefix  */
  315. X#define  sfileA  sfile[0]
  316. X#define  sfileB  sfile[1]
  317. X
  318. Xint  len[2];            /* Actual lines in each file  */
  319. X#define  lenA  len[0]
  320. X#define  lenB  len[1]
  321. X
  322. Xint  slen[2];        /* Squished lengths      */
  323. X#define  slenA  slen[0]
  324. X#define  slenB  slen[1]
  325. X
  326. Xint  prefix;            /* Identical lines at start  */
  327. Xint  suffix;            /* Identical lenes at end  */
  328. X
  329. XFILE  *infd[2] = { NULL, NULL };  /* Input file identifiers  */
  330. XFILE  *tempfd;        /* Temp for input redirection  */
  331. X
  332. Xextern long  ftell();
  333. Xextern FILE *fopen();
  334. X
  335. X#ifdef TURBO
  336. Xextern void *malloc();
  337. X#else
  338. Xextern char *malloc();
  339. X#endif
  340. X
  341. Xchar    *fgetss();
  342. Xunsigned short      hash();
  343. X
  344. X#ifdef    AMIGA
  345. X/* Define these types for Amiga C */
  346. Xchar    *savptr;
  347. Xint    savsiz;
  348. Xchar    *wrk;
  349. Xchar    *wrk2;
  350. Xint    cpysiz;
  351. X#endif
  352. X/*
  353. X * The following vectors overlay the area defined by fileA
  354. X */
  355. X
  356. Xshort      *class;      /* Unsorted line numbers  */
  357. Xint      *klist;      /* Index of element in clist  */
  358. XCANDIDATE  *clist;      /* Storage pool for candidates  */
  359. Xint      clength = 0;      /* Number of active candidates  */
  360. X#define    CSIZE_INC 50    /* How many to allocate each time we have to */
  361. Xint    csize = CSIZE_INC; /* Current size of storage pool */
  362. X
  363. Xint      *match;        /* Longest subsequence      */
  364. Xlong      *oldseek;      /* Seek position in file A  */
  365. X
  366. X/*
  367. X * The following vectors overlay the area defined by fileB
  368. X */
  369. X
  370. Xshort      *member;      /* Concatenated equiv. classes  */
  371. Xlong      *newseek;      /* Seek position in file B  */
  372. Xchar      *textb;        /* Input from file2 for check  */
  373. X
  374. X/*
  375. X * Global variables
  376. X */
  377. X
  378. Xint      eflag  = FALSE;  /* Edit script requested  */
  379. Xint      bflag  = FALSE;  /* Blank supress requested  */
  380. Xint      cflag  = FALSE;  /* Context printout      */
  381. Xint      iflag  = FALSE;  /* Ignore case requested  */
  382. Xchar      text[257];      /* Input line from file1  */
  383. Xextern char  *myalloc();      /* Storage allocator      */
  384. X
  385. Xextern char  *compact();      /* Storage compactor      */
  386. X
  387. X#ifdef  DEBUG
  388. X#ifndef OSK
  389. X#define  TIMING
  390. X#endif
  391. X#endif
  392. X#ifdef  TIMING
  393. Xextern long  time();
  394. Xextern char  *$$mend;
  395. Xlong      totaltime;
  396. Xlong      sectiontime;
  397. Xchar      *mstart;
  398. X#endif
  399. X
  400. Xmain(argc, argv)
  401. Xint      argc;
  402. Xchar      **argv;
  403. X/*
  404. X * Diff main program
  405. X */
  406. X{
  407. X  register int  i;
  408. X  register char  *ap;
  409. X
  410. X#ifdef OSK
  411. X  extern int _memmins;
  412. X  _memmins = 16 * 1024;            /* tell OSK we will malloc a lot */
  413. X#endif
  414. X#ifdef  TIMING
  415. X  sectiontime = time(&totaltime);
  416. X#endif
  417. X#ifdef vms
  418. X  argc = getredirection(argc,argv);
  419. X#endif
  420. X  while (argc > 1 && *(ap = argv[1]) == '-' && *++ap != EOS) {
  421. X      while (*ap != EOS) {
  422. X        switch ((*ap++)) {
  423. X        case 'b':
  424. X              bflag++;
  425. X              break;
  426. X
  427. X        case 'c':
  428. X          if (*ap > '0' && *ap <= '9') cflag = *ap++ - '0';
  429. X          else cflag = 3;
  430. X          break;
  431. X
  432. X        case 'e':
  433. X              eflag++;
  434. X              break;
  435. X
  436. X        case 'i':
  437. X              iflag++;
  438. X              break;
  439. X
  440. X        default:
  441. X              fprintf(stderr,
  442. X                  "Warning, bad option '%c'\n",
  443. X                  ap[-1]);
  444. X              break;
  445. X        }
  446. X      }
  447. X      argc--;
  448. X      argv++;
  449. X  }
  450. X
  451. X  if (argc != 3)
  452. X      error("Usage: diff [-options] file1 file2");
  453. X  if (cflag && eflag) {
  454. X      fprintf(stderr,
  455. X        "Warning, -c and -e are incompatible, -c supressed.\n");
  456. X      cflag = FALSE;
  457. X  }
  458. X  argv++;
  459. X  for (i = 0; i <= 1; i++) {
  460. X      if (argv[i][0] == '-' && argv[i][1] == EOS) {
  461. X        infd[i] = stdin;
  462. X        if ((tempfd = fopen(TEMPFILE, "w")) == NULL)
  463. X            cant(TEMPFILE, "work", 1);
  464. X      }
  465. X      else {
  466. X        infd[i] = fopen(argv[i], "r");
  467. X      if (!infd[i]) cant(argv[i], "input", 2);    /* Fatal error */
  468. X      }
  469. X  }
  470. X
  471. X  if (infd[0] == stdin && infd[1] == stdin)
  472. X    error("Can't diff two things both on standard input.");
  473. X
  474. X  if (infd[0] == NULL && infd[1] == NULL) {
  475. X      cant(argv[0], "input", 0);
  476. X      cant(argv[1], "input", 1);
  477. X  }
  478. X#ifdef vms
  479. X  else if (infd[1] == NULL)
  480. X      opendir(1, &argv[1], infd[0]);
  481. X  else if (infd[0] == NULL)
  482. X      opendir(0, &argv[0], infd[1]);
  483. X#endif
  484. X
  485. X /*
  486. X   * Read input, building hash tables.
  487. X   */
  488. X  input(0);
  489. X  input(1);
  490. X  squish();
  491. X#ifdef  DEBUG
  492. X  printf("before sort\n");
  493. X  for (i = 1; i <= slenA; i++)
  494. X      printf("sfileA[%d] = %6d %06o\n",
  495. X        i, sfileA[i].serial, sfileA[i].hash);
  496. X  for (i = 1; i <= slenB; i++)
  497. X      printf("sfileB[%d] = %6d %06o\n",
  498. X        i, sfileB[i].serial, sfileB[i].hash);
  499. X#endif
  500. X  sort(sfileA, slenA);
  501. X  sort(sfileB, slenB);
  502. X#ifdef  TIMING
  503. X  ptime("input");
  504. X#endif
  505. X#ifdef  DEBUG
  506. X  printf("after sort\n");
  507. X  for (i = 1; i <= slenA; i++)
  508. X      printf("sfileA[%d] = %6d %06o\n",
  509. X        i, sfileA[i].serial, sfileB[i].hash);
  510. X  for (i = 1; i <= slenB; i++)
  511. X      printf("sfileB[%d] = %6d %06o\n",
  512. X        i, sfileB[i].serial, sfileB[i].hash);
  513. X#endif
  514. X  /*
  515. X   * Build equivalence classes.
  516. X   */
  517. X  member = (short *)fileB;
  518. X  equiv();
  519. X  member = (short *)compact((char *)member, (slenB + 2) * sizeof (int),
  520. X        "squeezing member vector");
  521. X  /*
  522. X   * Reorder equivalence classes into array class[]
  523. X   */
  524. X  class = (short *)fileA;
  525. X  unsort();
  526. X  class = (short *)compact((char *)class, (slenA + 2) * sizeof (int),
  527. X        "compacting class vector");
  528. X#ifdef  TIMING
  529. X  ptime("equiv/unsort");
  530. X#endif
  531. X  /*
  532. X    * Find longest subsequences
  533. X   */
  534. X  klist = (int *)myalloc((slenA + 2) * sizeof (int), "klist");
  535. X  clist = (CANDIDATE *)myalloc(csize * sizeof (CANDIDATE), "clist");
  536. X  i = subseq();
  537. X#ifndef OSK
  538. X  free((char *)member);
  539. X  free((char *)class);
  540. X#else
  541. X  free((char *)member - sizeof(int));
  542. X  free((char *)class - sizeof(int));
  543. X#endif
  544. X  match = (int *)myalloc((lenA + 2) * sizeof (int), "match");
  545. X  unravel(klist[i]);
  546. X#ifndef OSK
  547. X  free((char *)clist);
  548. X  free((char *)klist);
  549. X#else
  550. X  free((char *)clist - sizeof(int));
  551. X  free((char *)klist - sizeof(int));
  552. X#endif
  553. X#ifdef  TIMING
  554. X  ptime("subsequence/unravel");
  555. X#endif
  556. X  /*
  557. X   * Check for fortuitous matches and output differences
  558. X   */
  559. X  oldseek = (long *)myalloc((lenA + 2) * sizeof (* oldseek), "oldseek");
  560. X  newseek = (long *)myalloc((lenB + 2) * sizeof (* newseek), "newseek");
  561. X  textb = myalloc(sizeof text, "textbuffer");
  562. X  if (check(argv[0], argv[1]))
  563. X      fprintf(stderr, "Spurious match, output is not optimal\n");
  564. X#ifdef  TIMING
  565. X  ptime("check");
  566. X#endif
  567. X  output(argv[0], argv[1]);
  568. X#ifdef  TIMING
  569. X  ptime("output");
  570. X  printf("%ld seconds required\n", sectiontime - totaltime);
  571. X#endif
  572. X  if (tempfd != NULL) {
  573. X      fclose(tempfd);
  574. X#ifdef unix
  575. X      unlink(TEMPFILE);
  576. X#else
  577. X#ifdef OSK
  578. X    unlink(TEMPFILE);
  579. X#else
  580. X    remove(TEMPFILE);
  581. X#endif
  582. X#endif 
  583. X  }
  584. X}
  585. X
  586. X
  587. Xinput(which)
  588. Xint      which;        /* 0 or 1 to redefine infd[]  */
  589. X/*
  590. X * Read the file, building hash table
  591. X */
  592. X{
  593. X  register LINE      *lentry;
  594. X  register int      linect = 0;
  595. X  FILE        *fd;
  596. X#define    LSIZE_INC 200    /* # of line entries to alloc at once */
  597. X  int    lsize = LSIZE_INC;
  598. X
  599. X  lentry = (LINE *)myalloc(sizeof(LINE) * (lsize+3), "line");
  600. X  fd = infd[which];
  601. X  while (!getline(fd, text)) {
  602. X    if (++linect >= lsize) {
  603. X        lsize += 200;
  604. X        lentry = (LINE *)compact((char *)lentry,
  605. X          (lsize + 3) * sizeof(LINE),
  606. X          "extending line vector");
  607. X    }
  608. X      lentry[linect].hash = hash(text);
  609. X  }
  610. X  /*
  611. X   * If input was from stdin ("-" command), finish off the temp file.
  612. X   */
  613. X  if (fd == stdin) {
  614. X      fclose(tempfd);
  615. X      tempfd = infd[which] = fopen(TEMPFILE, "r");
  616. X  }
  617. X  /* If we wanted to be stingy with memory, we could realloc lentry down
  618. X   * to its exact size (+3 for some odd reason) here.  No need?  */
  619. X  len[which] = linect;
  620. X  file[which] = lentry;
  621. X}
  622. X
  623. Xsquish()
  624. X/*
  625. X * Look for initial and trailing sequences that have identical hash values.
  626. X * Don't bother building them into the candidate vector.
  627. X */
  628. X{
  629. X  register int  i;
  630. X  register LINE  *ap;
  631. X  register LINE  *bp;
  632. X  int      j;
  633. X  int      k;
  634. X
  635. X  /*
  636. X   * prefix -> first line (from start) that doesn't hash identically
  637. X   */
  638. X  i = 0; ap = &fileA[1]; bp = &fileB[1];
  639. X  while (i < lenA && i < lenB && ap->hash == bp->hash) {
  640. X      i++; ap++; bp++;
  641. X  }
  642. X  prefix = i;
  643. X  /*
  644. X    * suffix -> first line (from end) that doesn't hash identically
  645. X   */
  646. X  j = lenA - i;
  647. X  k = lenB - i;
  648. X  ap = &fileA[lenA];
  649. X  bp = &fileB[lenB];
  650. X  i = 0;
  651. X  while (i < j && i < k && ap->hash == bp->hash) {
  652. X      i++; ap--; bp--;
  653. X  }
  654. X  suffix = i;
  655. X  /*
  656. X   * Tuck the counts away
  657. X   */
  658. X  for (k = 0; k <= 1; k++) {
  659. X      sfile[k] = file[k] + prefix;
  660. X      j = slen[k] = len[k] - prefix - suffix;
  661. X
  662. X      for (i = 0, ap = sfile[k]; i <= slen[k]; i++, ap++) {
  663. X        ap->serial = i;
  664. X      }
  665. X  }
  666. X}
  667. X
  668. Xsort(vector, vecsize)
  669. XLINE      *vector;      /* What to sort        */
  670. Xint      vecsize;      /* How many to sort      */
  671. X/*
  672. X * Sort hash entries
  673. X */
  674. X{
  675. X  register int  j;
  676. X  register LINE  *aim;
  677. X  register LINE  *ai;
  678. X  int      mid;  
  679. X  int      k;
  680. X  LINE      work;
  681. X
  682. X  for (j = 1; j <= vecsize; j *= 2);
  683. X  mid = (j - 1);
  684. X  while ((mid /= 2) != 0) {
  685. X      k = vecsize - mid;
  686. X      for (j = 1; j <= k; j++) {
  687. X        for (ai = &vector[j]; ai > vector; ai -= mid) {
  688. X            aim = &ai[mid];
  689. X            if (aim < ai)
  690. X              break;  /* ?? Why ??      */
  691. X            if (aim->hash > ai->hash ||
  692. X                  aim->hash == ai->hash &&
  693. X                  aim->serial > ai->serial)
  694. X              break;
  695. X            work.hash = ai->hash;
  696. X            ai->hash = aim->hash;
  697. X            aim->hash = work.hash;
  698. X            work.serial = ai->serial;
  699. X            ai->serial = aim->serial;
  700. X            aim->serial = work.serial;
  701. X        }
  702. X      }
  703. X  }
  704. X}
  705. X
  706. Xequiv()
  707. X/*
  708. X * Build equivalence class vector
  709. X */
  710. X{
  711. X  register LINE  *ap;
  712. X#ifdef TURBO
  713. X  union {
  714. X#else
  715. X  register union {
  716. X#endif
  717. X      LINE    *bp;
  718. X      short    *mp;
  719. X  } r;
  720. X  register int  j;
  721. X  LINE    *atop;
  722. X
  723. X#ifdef  DEBUG
  724. X  printf("equiv entry\n");
  725. X  for (j = 1; j <= slenA; j++)
  726. X      printf("sfileA[%d] = %6d %06o\n",
  727. X            j, sfileA[j].serial, sfileA[j].hash);
  728. X  for (j = 1; j <= slenB; j++)
  729. X      printf("sfileB[%d] = %6d %06o\n",
  730. X            j, sfileB[j].serial, sfileB[j].hash);
  731. X#endif
  732. X  j = 1;
  733. X  ap = &sfileA[1];
  734. X  r.bp = &sfileB[1];
  735. X  atop = &sfileA[slenA];
  736. X  while (ap <= atop && j <= slenB) {
  737. X      if (ap->hash < r.bp->hash) {
  738. X        ap->hash = 0;
  739. X        ap++;
  740. X      }
  741. X      else if (ap->hash == r.bp->hash) {
  742. X        ap->hash = j;
  743. X        ap++;
  744. X      }
  745. X      else {
  746. X        r.bp++;
  747. X        j++;
  748. X      }
  749. X  }
  750. X  while (ap <= atop) {
  751. X      ap->hash = 0;
  752. X      ap++;
  753. X  }
  754. X  sfileB[slenB + 1].hash = 0;
  755. X#ifdef  DEBUG
  756. X  printf("equiv exit\n");
  757. X  for (j = 1; j <= slenA; j++)
  758. X      printf("sfileA[%d] = %6d %06o\n",
  759. X            j, sfileA[j].serial, sfileA[j].hash);
  760. X  for (j = 1; j <= slenB; j++)
  761. X      printf("sfileB[%d] = %6d %06o\n",
  762. X            j, sfileB[j].serial, sfileB[j].hash);
  763. X#endif
  764. X  ap = &sfileB[0];
  765. X  atop = &sfileB[slenB];
  766. X  r.mp = &member[0];
  767. X  while (++ap <= atop) {
  768. X      r.mp++;
  769. X      *r.mp = -(ap->serial);
  770. X      while (ap[1].hash == ap->hash) {
  771. X        ap++;
  772. X        r.mp++;
  773. X        *r.mp = ap->serial;
  774. X      }
  775. X  }
  776. X  r.mp[1] = -1;
  777. X#ifdef  DEBUG
  778. X  for (j = 0; j <= slenB; j++)
  779. X      printf("member[%d] = %d\n", j, member[j]);
  780. X#endif
  781. X}
  782. X
  783. Xunsort()
  784. X/*
  785. X * Build class vector
  786. X */
  787. X{
  788. X  register int  *temp;
  789. X  register int  *tp;
  790. X#if TURBO
  791. X  union {
  792. X#else
  793. X  register union {
  794. X#endif
  795. X      LINE    *ap;
  796. X      short    *cp;
  797. X  } u;
  798. X  LINE    *evec;
  799. X  short    *eclass;
  800. X#ifdef  DEBUG
  801. X  int      i;
  802. X#endif
  803. X
  804. X  temp = (int *)myalloc((slenA + 1) * sizeof(int), "unsort scratch");
  805. X  u.ap = &sfileA[1];
  806. X  evec = &sfileA[slenA];
  807. X  while (u.ap <= evec) {
  808. X#ifdef  DEBUG
  809. X      printf("temp[%2d] := %06o\n", u.ap->serial, u.ap->hash);
  810. X#endif
  811. X      temp[u.ap->serial] = u.ap->hash;
  812. X      u.ap++;
  813. X  }
  814. X  /*
  815. X   * Copy into class vector and free work space
  816. X   */
  817. X  u.cp = &class[1];
  818. X  eclass = &class[slenA];
  819. X  tp = &temp[1];
  820. X  while (u.cp <= eclass)
  821. X      *u.cp++ = *tp++;
  822. X#ifndef OSK
  823. X  free((char *) temp);
  824. X#else
  825. X  free((char *)temp - sizeof(int));
  826. X#endif
  827. X#ifdef  DEBUG
  828. X  printf("unsort exit\n");
  829. X  for (i = 1; i <= slenA; i++)
  830. X      printf("class[%d] = %d %06o\n", i, class[i], class[i]);
  831. X#endif
  832. X}
  833. X
  834. Xsubseq()
  835. X/*
  836. X * Generate maximum common subsequence chain in clist[]
  837. X */
  838. X{
  839. X  int        a;
  840. X  register unsigned      ktop;
  841. X  register int      b;
  842. X  register int      s;
  843. X  unsigned        r;
  844. X  int        i;
  845. X  int        cand;
  846. X
  847. X  klist[0] = newcand(0, 0, -1);
  848. X  klist[1] = newcand(slenA + 1, slenB + 1, -1);
  849. X  ktop = 1;            /* -> guard entry  */
  850. X  for (a = 1; a <= slenA; a++) {
  851. X      /*
  852. X       * For each non-zero element in fileA ...
  853. X       */
  854. X      if ((i = class[a]) == 0)
  855. X        continue;
  856. X      cand = klist[0];      /* No candidate now  */
  857. X      r = 0;            /* Current r-candidate  */
  858. X      do {
  859. X#ifdef  DEBUG
  860. X        printf("a = %d, i = %d, b = %d\n", a, i, member[i]);
  861. X#endif
  862. X        /*
  863. X         * Perform the merge algorithm
  864. X         */
  865. X        if ((b = member[i]) < 0)
  866. X            b = -b;
  867. X#ifdef  DEBUG
  868. X        printf("search(%d, %d, %d) -> %d\n",
  869. X              r, ktop, b, search(r, ktop, b));
  870. X#endif
  871. X        if ((s = search(r, ktop, b)) != 0) {
  872. X            if (clist[klist[s]].b > b) {
  873. X              klist[r] = cand;
  874. X              r = s;
  875. X              cand = newcand(a, b, klist[s-1]);
  876. X#ifdef  DEBUG
  877. X              dumpklist(ktop, "klist[s-1]->b > b");
  878. X#endif
  879. X            }
  880. X            if (s >= ktop) {
  881. X              klist[ktop + 1] = klist[ktop];
  882. X              ktop++;
  883. X#ifdef  DEBUG
  884. X              klist[r] = cand;
  885. X              dumpklist(ktop, "extend");
  886. X#endif
  887. X              break;
  888. X            }
  889. X        }
  890. X      } while (member[++i] > 0);
  891. X    klist[r] = cand;
  892. X  }
  893. X#ifdef  DEBUG
  894. X  printf("last entry = %d\n", ktop - 1);
  895. X#endif
  896. X  return(ktop - 1);        /* Last entry found  */
  897. X}
  898. X
  899. Xint
  900. Xnewcand(a, b, pred)
  901. Xint      a;      /* Line in fileA        */
  902. Xint      b;      /* Line in fileB        */
  903. Xint      pred;      /* Link to predecessor, index in cand[]  */
  904. X{
  905. X  register CANDIDATE  *new;
  906. X
  907. X  clength++;
  908. X  if (++clength >= csize) {
  909. X    csize += CSIZE_INC;
  910. X    clist = (CANDIDATE *)compact((char *)clist,
  911. X          csize * sizeof (CANDIDATE),
  912. X          "extending clist");
  913. X  }
  914. X  new = &clist[clength - 1];
  915. X  new->a = a;
  916. X  new->b = b;
  917. X  new->link = pred;
  918. X  return(clength - 1);
  919. X}
  920. X
  921. Xsearch(low, high, b)
  922. Xregister unsigned  low;
  923. Xregister unsigned  high;
  924. Xregister int  b;
  925. X/*
  926. X * Search klist[low..top] (inclusive) for b.  If klist[low]->b >= b,
  927. X * return zero.  Else return s such that klist[s-1]->b < b and
  928. X * klist[s]->b >= b.  Note that the algorithm presupposes the two
  929. X * preset "fence" elements, (0, 0) and (slenA, slenB).
  930. X */
  931. X{
  932. X  register int      temp;
  933. X  register unsigned      mid;
  934. X
  935. X  if (clist[klist[low]].b >= b)
  936. X      return(0);
  937. X  while ((mid = (low + high) / 2) > low) {
  938. X      if ((temp = clist[klist[mid]].b) > b)
  939. X        high = mid;
  940. X      else if (temp < b)
  941. X        low = mid;
  942. X      else {
  943. X        return(mid);
  944. X      }
  945. X  }
  946. X  return(mid + 1);
  947. X}
  948. X
  949. X
  950. Xunravel(k)
  951. Xregister int  k;
  952. X{
  953. X  register int      i;
  954. X  register CANDIDATE  *cp;
  955. X  int        first_trailer;
  956. X  int        difference;
  957. X
  958. X  first_trailer = lenA - suffix;
  959. X  difference = lenB - lenA;
  960. X#ifdef  DEBUG
  961. X  printf("first trailer = %d, difference = %d\n",
  962. X      first_trailer, difference);
  963. X#endif
  964. X  for (i = 0; i <= lenA; i++) {
  965. X      match[i] = (i <= prefix) ? i
  966. X        : (i > first_trailer) ? i + difference
  967. X        : 0;
  968. X  }
  969. X#ifdef  DEBUG
  970. X  printf("unravel\n");
  971. X#endif
  972. X  while (k != -1) {
  973. X      cp = &clist[k];
  974. X#ifdef  DEBUG
  975. X      if (k < 0 || k >= clength)
  976. X        error("Illegal link -> %d", k);
  977. X      printf("match[%d] := %d\n", cp->a + prefix, cp->b + prefix);
  978. X#endif
  979. X      match[cp->a + prefix] = cp->b + prefix;
  980. X      k = cp->link;
  981. X  }
  982. X}
  983. X
  984. X
  985. X/*
  986. X * Check for hash matches (jackpots) and collect random access indices to
  987. X * the two files.
  988. X *
  989. X * It should be possible to avoid doing most of the ftell's by noticing
  990. X * that we are not doing a context diff and noticing that if a line
  991. X * compares equal to the other file, we will not ever need to know its
  992. X * file position.  FIXME.
  993. X */
  994. Xcheck(fileAname, fileBname)
  995. Xchar      *fileAname;
  996. Xchar      *fileBname;
  997. X{
  998. X  register int  a;      /* Current line in file A  */
  999. X  register int  b;      /* Current line in file B  */
  1000. X  int      jackpot;
  1001. X
  1002. X/*
  1003. X * The VAX C ftell() returns the address of the CURRENT record, not the
  1004. X * next one (as in DECUS C or, effectively, other C's).  Hence, the values
  1005. X * are "off by one" in the array.  OFFSET compensates for this.
  1006. X */
  1007. X#ifdef vms
  1008. X#define OFFSET (-1)
  1009. X#else
  1010. X#define OFFSET 0
  1011. X#endif
  1012. X
  1013. X  b = 1;
  1014. X  rewind(infd[0]);
  1015. X  rewind(infd[1]);
  1016. X/*
  1017. X * See above; these would be over-written on VMS anyway.
  1018. X */
  1019. X#ifndef vms
  1020. X  oldseek[0] = ftell(infd[0]);
  1021. X  newseek[0] = ftell(infd[1]);
  1022. X#endif
  1023. X
  1024. X  jackpot = 0;
  1025. X#ifdef  DEBUG
  1026. X  printf("match vector\n");
  1027. X  for (a = 0; a <= lenA; a++)
  1028. X      printf("match[%d] = %d\n", a, match[a]);
  1029. X#endif
  1030. X  for (a = 1; a <= lenA; a++) {
  1031. X      if (match[a] == 0) {
  1032. X      /* Unique line in A */
  1033. X        oldseek[a+OFFSET] = ftell(infd[0]);
  1034. X        getline(infd[0], text);
  1035. X        continue;  
  1036. X      }
  1037. X      while (b < match[a]) {
  1038. X      /* Skip over unique lines in B */
  1039. X        newseek[b+OFFSET] = ftell(infd[1]);
  1040. X        getline(infd[1], textb);
  1041. X        b++;
  1042. X      }
  1043. X
  1044. X    /*
  1045. X     * Compare the two, supposedly matching, lines.
  1046. X     * Unless we are going to print these lines, don't bother to
  1047. X     * remember where they are.  We only print matching lines
  1048. X     * if a context diff is happening, or if a jackpot occurs.
  1049. X     */
  1050. X    if (cflag) {
  1051. X        oldseek[a+OFFSET] = ftell(infd[0]);
  1052. X        newseek[b+OFFSET] = ftell(infd[1]);
  1053. X    }
  1054. X      getline(infd[0], text);
  1055. X      getline(infd[1], textb);
  1056. X      if (!streq(text, textb)) {
  1057. X        fprintf(stderr,  "Spurious match:\n");
  1058. X        fprintf(stderr, "line %d in %s, \"%s\"\n",
  1059. X            a, fileAname, text);
  1060. X        fprintf(stderr, "line %d in %s, \"%s\"\n",
  1061. X            b, fileBname, textb);
  1062. X        match[a] = 0;
  1063. X        jackpot++;
  1064. X      }
  1065. X
  1066. X      b++;
  1067. X  }
  1068. X  for (; b <= lenB; b++) {
  1069. X      newseek[b+OFFSET] = ftell(infd[1]);
  1070. X      getline(infd[1], textb);
  1071. X  }
  1072. X/*
  1073. X * The logical converse to the code up above, for NON-VMS systems, to
  1074. X * store away an fseek() pointer at the beginning of the file.  For VMS,
  1075. X * we need one at EOF...
  1076. X */
  1077. X#ifdef vms
  1078. X  oldseek[lenA] = ftell(infd[0]);
  1079. X  getline(infd[0],text);        /* Will hit EOF...  */
  1080. X  newseek[lenB] = ftell(infd[1]);
  1081. X  getline(infd[1],textb);        /* Will hit EOF...  */
  1082. X#endif
  1083. X
  1084. X  return(jackpot);
  1085. X}
  1086. X
  1087. Xoutput(fileAname, fileBname)
  1088. Xchar *fileAname, *fileBname;
  1089. X{
  1090. X  register int  astart;
  1091. X  register int  aend = 0;
  1092. X  int      bstart;
  1093. X  register int  bend;
  1094. X
  1095. X  rewind(infd[0]);
  1096. X  rewind(infd[1]);
  1097. X  match[0] = 0;
  1098. X  match[lenA+1] = lenB + 1;
  1099. X  if (!eflag) {
  1100. X    if (cflag) {
  1101. X        /*
  1102. X         * Should include ctime style dates after the file names, but
  1103. X         * this would be non-trivial on OSK.  Perhaps there should be
  1104. X         * a special case for stdin.
  1105. X         */
  1106. X        printf("*** %s\n--- %s\n", fileAname, fileBname);
  1107. X    }
  1108. X      /*
  1109. X       * Normal printout
  1110. X       */
  1111. X      for (astart = 1; astart <= lenA; astart = aend + 1) {
  1112. X        /*
  1113. X         * New subsequence, skip over matching stuff
  1114. X         */
  1115. X        while (astart <= lenA
  1116. X              && match[astart] == (match[astart - 1] + 1))
  1117. X            astart++;
  1118. X        /*
  1119. X         * Found a difference, setup range and print it
  1120. X         */
  1121. X        bstart = match[astart - 1] + 1;
  1122. X        aend = astart - 1;
  1123. X        while (aend < lenA && match[aend + 1] == 0)
  1124. X            aend++;
  1125. X        bend = match[aend + 1] - 1;
  1126. X        match[aend] = bend;
  1127. X        change(astart, aend, bstart, bend);
  1128. X      }
  1129. X  }
  1130. X  else {
  1131. X      /*
  1132. X       * Edit script output -- differences are output "backwards"
  1133. X       * for the benefit of a line-oriented editor.
  1134. X       */
  1135. X      for (aend = lenA; aend >= 1; aend = astart - 1) {
  1136. X        while (aend >= 1
  1137. X              && match[aend] == (match[aend + 1] - 1)
  1138. X              && match[aend] != 0)
  1139. X            aend--;
  1140. X        bend = match[aend + 1] - 1;
  1141. X        astart = aend + 1;
  1142. X        while (astart > 1 && match[astart - 1] == 0)
  1143. X            astart--;
  1144. X        bstart = match[astart - 1] + 1;
  1145. X        match[astart] = bstart;
  1146. X        change(astart, aend, bstart, bend);
  1147. X      }
  1148. X  }
  1149. X  if (lenA == 0)
  1150. X      change(1, 0, 1, lenB);
  1151. X}
  1152. X
  1153. X
  1154. X/*
  1155. X * Output a change entry: fileA[astart..aend] changed to fileB[bstart..bend]
  1156. X */
  1157. Xchange(astart, aend, bstart, bend)
  1158. Xint      astart;
  1159. Xint      aend;
  1160. Xint      bstart;
  1161. Xint      bend;
  1162. X{
  1163. X  char c;
  1164. X  /*
  1165. X   * This catches a "dummy" last entry
  1166. X   */
  1167. X  if (astart > aend && bstart > bend)
  1168. X      return;
  1169. X  c = (astart > aend) ? 'a' : (bstart > bend) ? 'd' : 'c';
  1170. X  if (cflag) fputs("**************\n*** ", stdout);
  1171. X
  1172. X  if (c == 'a' && !cflag)
  1173. X    range(astart-1, astart-1, 0);    /* Addition: just print one odd # */
  1174. X  else
  1175. X    range(astart, aend, 0);        /* Print both, if different */
  1176. X  if (!cflag) {
  1177. X    putchar(c);
  1178. X    if (!eflag) {
  1179. X    if (c == 'd')
  1180. X        range(bstart-1, bstart-1, 1); /* Deletion: just print one odd # */
  1181. X    else
  1182. X        range(bstart, bend, 1);    /* Print both, if different */
  1183. X    }
  1184. X  }
  1185. X  putchar('\n');
  1186. X  if (!eflag) {
  1187. X      fetch(oldseek, astart, aend, lenA, infd[0], 
  1188. X        cflag ? (c=='d' ? "- " : "! ") : "< ");
  1189. X    if (cflag) {
  1190. X      fputs("--- ", stdout);
  1191. X      range(bstart, bend, 1);
  1192. X      fputs(" -----\n", stdout);
  1193. X    } else if (astart <= aend && bstart <= bend)
  1194. X        printf("---\n");
  1195. X  }
  1196. X  fetch(newseek, bstart, bend, lenB, infd[1], 
  1197. X      cflag ? (c=='a' ? "+ " : "! ") : (eflag ? "" : "> "));
  1198. X  if (eflag && bstart <= bend)
  1199. X      printf(".\n");
  1200. X}
  1201. X
  1202. X
  1203. Xrange(from, to, w)
  1204. Xint      from;
  1205. Xint      to;
  1206. Xint    w;
  1207. X/*
  1208. X * Print a range
  1209. X */
  1210. X{
  1211. X  if (cflag) {
  1212. X    if((from -= cflag) <= 0) from = 1;
  1213. X    if((to += cflag) > len[w]) to = len[w];
  1214. X  }
  1215. X  if (to > from) {
  1216. X    printf("%d,%d", from, to);
  1217. X  } else if (to < from) {
  1218. X    printf("%d,%d", to, from);
  1219. X  } else {
  1220. X    printf("%d", from);
  1221. X  }
  1222. X}
  1223. X
  1224. X
  1225. Xfetch(seekvec, start, end, trueend, fd, pfx)
  1226. Xlong      *seekvec;
  1227. Xregister int  start;
  1228. Xregister int  end;
  1229. Xint      trueend;
  1230. XFILE      *fd;
  1231. Xchar      *pfx;
  1232. X/*
  1233. X * Print the appropriate text
  1234. X */
  1235. X{
  1236. X  register int  i;
  1237. X  register int    first;
  1238. X  register int    last;
  1239. X
  1240. X  if (cflag) {
  1241. X    if((first = start - cflag) <= 0) first = 1;
  1242. X    if((last = end + cflag) > trueend) last = trueend;
  1243. X  } else {
  1244. X    first = start;
  1245. X    last = end;
  1246. X  }
  1247. X  if (fseek(fd, seekvec[first], 0) != 0) {
  1248. X      printf("?Can't read line %d at %08lx (hex) in file%c\n",
  1249. X        start, seekvec[first],
  1250. X        (fd == infd[0]) ? 'A' : 'B');
  1251. X  }
  1252. X  else {
  1253. X      for (i = first; i <= last; i++) {
  1254. X        if (fgetss(text, sizeof text, fd) == NULL) {
  1255. X            printf("** Unexpected end of file\n");
  1256. X            break;
  1257. X        }
  1258. X#ifdef DEBUG
  1259. X        printf("%5d: %s%s\n", i, pfx, text);
  1260. X#else
  1261. X        fputs((cflag && (i<start || i>end)) ? "  " : pfx, stdout);
  1262. X        fputs(text, stdout);
  1263. X      putchar('\n');
  1264. X#endif
  1265. X      }
  1266. X  }  
  1267. X}
  1268. X
  1269. X
  1270. X/*
  1271. X * Input routine, read one line to buffer[], return TRUE on eof, else FALSE.
  1272. X * The terminating newline is always removed.  If "-b" was given, trailing
  1273. X * whitespace (blanks and tabs) are removed and strings of blanks and
  1274. X * tabs are replaced by a single blank.  Getline() does all hacking for
  1275. X * redirected input files.
  1276. X */
  1277. Xint
  1278. Xgetline(fd, buffer)
  1279. XFILE      *fd;
  1280. Xchar      *buffer;
  1281. X{
  1282. X  register char  *top;
  1283. X  register char  *fromp;
  1284. X  register char  c;
  1285. X
  1286. X  if (fgetss(buffer, sizeof text, fd) == NULL) {
  1287. X      *buffer = EOS;
  1288. X      return(TRUE);
  1289. X  }
  1290. X  if (fd == stdin)
  1291. X      fputss(buffer, tempfd);
  1292. X  if (bflag || iflag) {
  1293. X      top = buffer;
  1294. X      fromp = buffer;
  1295. X      while ((c = *fromp++) != EOS) {
  1296. X        if (bflag && (c == ' ' || c == '\t')) {
  1297. X            c = ' ';
  1298. X            while (*fromp == ' ' || *fromp == '\t')
  1299. X              fromp++;
  1300. X        }
  1301. X        if (iflag)
  1302. X            c = tolower(c);
  1303. X        *top++ = c;
  1304. X      }
  1305. X      if (bflag && top[-1] == ' ')
  1306. X        top--;
  1307. X      *top = EOS;
  1308. X  }
  1309. X  return(FALSE);
  1310. X}
  1311. Xstatic unsigned short crc16a[] = {
  1312. X  0000000,  0140301,  0140601,  0000500,
  1313. X  0141401,  0001700,  0001200,  0141101,
  1314. X  0143001,  0003300,  0003600,  0143501,
  1315. X  0002400,  0142701,  0142201,  0002100,  
  1316. X};
  1317. Xstatic unsigned short crc16b[] = {
  1318. X  0000000,  0146001,  0154001,  0012000,
  1319. X  0170001,  0036000,  0024000,  0162001,
  1320. X  0120001,  0066000,  0074000,  0132001,
  1321. X  0050000,  0116001,  0104001,  0043000,
  1322. X};
  1323. X
  1324. Xunsigned short
  1325. Xhash(buffer)
  1326. Xchar      *buffer;
  1327. X/*
  1328. X * Return the CRC16 hash code for the buffer
  1329. X * Algorithm from Stu Wecker (Digital memo 130-959-002-00).
  1330. X */
  1331. X{
  1332. X  register unsigned short  crc;
  1333. X  register char      *tp;
  1334. X  register short       temp;
  1335. X
  1336. X  crc = 0;
  1337. X  for (tp = buffer; *tp != EOS;) {
  1338. X      temp = *tp++ ^ crc;  /* XOR crc with new char  */
  1339. X      crc = (crc >> 8)
  1340. X        ^ crc16a[(temp & 0017)]
  1341. X        ^ crc16b[(temp & 0360) >> 4];
  1342. X  }
  1343. X#ifdef  DEBUG_ALL
  1344. X  printf("%06o: %s\n", (crc == 0) ? 1 : crc, buffer);
  1345. X#endif
  1346. X  return((crc == 0) ? (unsigned short) 1 : crc);
  1347. X}      
  1348. X
  1349. X
  1350. X#ifdef vms
  1351. Xopendir(which, arg, okfd)
  1352. Xint      which;      /* Which file to open (0 or 1)      */
  1353. Xchar      **arg;      /* File name argument, &argv[which]  */
  1354. XFILE      *okfd;      /* File name (already open)      */
  1355. X{
  1356. X  register char      *tp;
  1357. X  register int      c;
  1358. X  register char      *newname;
  1359. X
  1360. X  fgetname(okfd, text);
  1361. X  /*
  1362. X   * Skip over device name
  1363. X   */
  1364. X  for (tp = text; (c = *tp) && c != ':'; tp++);
  1365. X  if (c)  tp++;
  1366. X  else  tp = text;
  1367. X  /*
  1368. X   * Skip over [UIC] or [PPN] if present
  1369. X   */
  1370. X  if (*tp == '[' || *tp == '(') {
  1371. X      while ((c = *tp++) && c != ']' && c != ')');
  1372. X      if (c == 0) {
  1373. X        fprintf(stderr, "?Bug: bad file name \"%s\"\n",
  1374. X              text);
  1375. X        tp--;
  1376. X      }
  1377. X  }
  1378. X  strcpy(text, tp);
  1379. X  /*
  1380. X   * Don't include version
  1381. X   */
  1382. X  for (tp = text; (c = *tp) && c != ';'; tp++);
  1383. X  *tp = 0;
  1384. X  /*
  1385. X   * Now, text has the file name, tp - text, its length,
  1386. X   * and *arg the (possible) directory name.  Create a new
  1387. X   * file name for opening.
  1388. X   */
  1389. X#ifndef    OSK
  1390. X  if ((newname = malloc(tp - text + strlen(*arg) + 1)) == NULL)
  1391. X      error("Out of space at start");
  1392. X#ifdef    AMIGA
  1393. X    savsiz = tp - text + strlen(*arg) + 1;
  1394. X    savptr = newname;
  1395. X#endif
  1396. X#else
  1397. X  newname = myalloc(tp - text + strlen(*arg) + 1, "Out of space at start");
  1398. X#endif
  1399. X  concat(newname, *arg, text, NULL);
  1400. X  if ((infd[which] = fopen(newname, "r")) == NULL)
  1401. X      cant(*arg, "constructed input", 1);
  1402. X  else
  1403. X      *arg = newname;
  1404. X}
  1405. X/* Amiga C doesn't have all these extensions for directory... */
  1406. X#endif
  1407. X
  1408. Xchar *
  1409. Xmyalloc(amount, why)
  1410. Xint      amount;
  1411. Xchar      *why;
  1412. X/*
  1413. X * Allocate or crash.
  1414. X */
  1415. X{
  1416. X  register char  *pointer;
  1417. X
  1418. X#ifdef OSK
  1419. X  amount += sizeof(int);
  1420. X#endif
  1421. X  if ((pointer = malloc((unsigned) amount)) == NULL)
  1422. X      noroom(why);
  1423. X#ifdef OSK
  1424. X  *((int *)pointer) = amount;
  1425. X  pointer += sizeof(int);
  1426. X#ifdef DEBUG
  1427. X  fprintf(stderr, "Myalloc: %d at %06o\n", amount, pointer);
  1428. X#endif
  1429. X#endif
  1430. X#ifdef    AMIGA
  1431. X   savsiz =  amount;
  1432. X   savptr = pointer;
  1433. X#endif
  1434. X
  1435. X  return (pointer);
  1436. X}
  1437. X
  1438. X
  1439. X/*
  1440. X * Reallocate pointer, compacting storage
  1441. X *
  1442. X * The "compacting storage" part is probably not relevant any more.
  1443. X * There used to be horrid code here that malloc'd one byte and freed
  1444. X * it at magic times, to cause garbage collection of the freespace
  1445. X * or something.  It's safely gone now, you didn't have to see it.
  1446. X *    -- John Gilmore, Nebula Consultants, Sept 26, 1986
  1447. X */
  1448. Xchar *
  1449. Xcompact(pointer, new_amount, why)
  1450. Xchar      *pointer;
  1451. Xint      new_amount;
  1452. Xchar      *why;
  1453. X{
  1454. X  register char *new_pointer;
  1455. X
  1456. X#ifndef AMIGA
  1457. X#ifndef OSK
  1458. X#ifdef TURBO
  1459. X  extern void *realloc();
  1460. X#else
  1461. X  extern char *realloc();
  1462. X#endif
  1463. X
  1464. X  if ((new_pointer =  realloc(pointer, (unsigned) new_amount)) == NULL){
  1465. X      noroom(why);
  1466. X  }
  1467. X#else
  1468. X  register int old_amount;
  1469. X  new_amount += sizeof(int);
  1470. X  if((new_pointer = malloc(new_amount)) == NULL) noroom(why);
  1471. X  *(int *)new_pointer = new_amount;
  1472. X  new_pointer += sizeof(int);
  1473. X  old_amount = *(((int *)pointer)-1);
  1474. X  /* _strass is like bcopy with the first two arguments reversted */
  1475. X  _strass(new_pointer, pointer, (new_amount <= old_amount ?
  1476. X      new_amount : old_amount) - sizeof(int));
  1477. X#ifdef DEBUG
  1478. X  fprintf(stderr, "compact %d to %d from %06o to %06o\n", 
  1479. X    old_amount, new_amount, pointer, new_pointer);
  1480. X#endif
  1481. X  free(pointer - sizeof(int));
  1482. X#endif
  1483. X#else
  1484. X  /*
  1485. X   * This routine is heavily dependent on C storage allocator hacks
  1486. X   * For Amiga, we can't rely on storage being left alone "up to"
  1487. X   * the boundary of allocation as in VMS or RSX. Therefore we have
  1488. X   * to be different here and allocate a new larger segment, then
  1489. X   * free the old one. Messy but hopefully it will work.
  1490. X   */
  1491. X  extern char  *malloc();
  1492. X
  1493. X  /* No realloc().  Do a malloc and copy it.  */
  1494. X  if ((new_pointer = malloc((unsigned) new_amount)) == NULL){
  1495. X      noroom(why);
  1496. X  }
  1497. X/* This MUST assume the program calls compact using the old pointer as the
  1498. X  last call of malloc... Reason is that RSX version is really simpleminded */
  1499. X   cpysiz=savsiz;
  1500. X/* Complain if deallocate area not same as last allocate area */
  1501. X if (savptr != pointer) bogus(why);
  1502. X   wrk2=new_pointer;
  1503. X   for (wrk=pointer;cpysiz > 0;cpysiz--){
  1504. X/* copy data to new area */
  1505. X     *wrk2++ = *wrk++;
  1506. X     }
  1507. X/* when done, free old memory area. */
  1508. X   free(pointer);
  1509. X#endif
  1510. X
  1511. X#ifndef OSK
  1512. X#ifdef  DEBUG
  1513. X  if (new_pointer != pointer) {
  1514. X      fprintf(stderr, "moved from %06o to %06o\n",
  1515. X        pointer, new_pointer);
  1516. X  }
  1517. X/*  rdump(new_pointer, why);
  1518. X*/
  1519. X#endif
  1520. X#endif
  1521. X  return (new_pointer);
  1522. X}
  1523. X
  1524. Xnoroom(why)
  1525. Xchar      *why;
  1526. X{
  1527. X  fprintf(stderr, "?DIFF-F-out of room when %s\n", why);
  1528. X  exit(IO_ERROR);
  1529. X}
  1530. X
  1531. X#ifdef    AMIGA
  1532. Xbogus(why)
  1533. Xchar      *why;
  1534. X{
  1535. X  fprintf(stderr, "?DIFF-F-invalid compaction when %s\n", why);
  1536. X  exit(IO_ERROR);
  1537. X}
  1538. X#endif
  1539. X
  1540. X#ifdef  DEBUG
  1541. Xrdump(pointer, why)
  1542. Xint      *pointer;
  1543. Xchar      *why;
  1544. X/*
  1545. X * Dump memory block
  1546. X */
  1547. X{
  1548. X  int  *last;
  1549. X  int  count;
  1550. X
  1551. X  last = ((int **)pointer)[-1];
  1552. X  fprintf(stderr, "dump %s of %06o -> %06o, %d words",
  1553. X        why, pointer, last, last - pointer);
  1554. X  last = (int *)(((int) last) & ~1);
  1555. X  for (count = 0; pointer < last; ++count) {
  1556. X      if ((count & 07) == 0) {
  1557. X        fprintf(stderr, "\n%06o", pointer);
  1558. X      }
  1559. X      fprintf(stderr, "\t%06o", *pointer);
  1560. X      pointer++;
  1561. X  }
  1562. X  fprintf(stderr, "\n");
  1563. X}
  1564. X#endif
  1565. Xcant(filename, what, fatalflag)
  1566. Xchar      *filename;
  1567. Xchar      *what;
  1568. Xint      fatalflag;
  1569. X/*
  1570. X * Can't open file message
  1571. X */
  1572. X{
  1573. X  fprintf(stderr, "Can't open %s file \"%s\": ", what, filename);
  1574. X#ifndef    OSK
  1575. X  perror((char *)NULL);
  1576. X#else
  1577. X  prerr(0, errno);
  1578. X#endif
  1579. X  if (fatalflag) {
  1580. X      exit(fatalflag);
  1581. X  }
  1582. X}
  1583. X#ifdef  DEBUG
  1584. Xdump(d_linep, d_len, d_which)
  1585. XLINE  *d_linep;
  1586. X{
  1587. X  register int i;
  1588. X  
  1589. X  printf("Dump of file%c, %d elements\n", "AB"[d_which], d_len);
  1590. X  printf("linep @ %06o\n", d_linep);
  1591. X  for (i = 0; i <= d_len; i++) {
  1592. X      printf("%3d  %6d  %06o\n", i,
  1593. X            d_linep[i].serial, d_linep[i].hash);
  1594. X  }
  1595. X}
  1596. X
  1597. Xdumpklist(kmax, why)
  1598. Xint  kmax;
  1599. Xchar  *why;
  1600. X/*
  1601. X * Dump klist
  1602. X */
  1603. X{
  1604. X  register int      i;
  1605. X  register CANDIDATE  *cp;
  1606. X  register int      count;
  1607. X
  1608. X  printf("\nklist[0..%d] %s, clength = %d\n", kmax, why, clength);
  1609. X  for (i = 0; i <= kmax; i++) {
  1610. X      cp = &clist[klist[i]];
  1611. X      printf("%2d %2d", i, klist[i]);
  1612. X      if (cp >= &clist[0] && cp < &clist[clength])
  1613. X        printf(" (%2d %2d -> %2d)\n", cp->a, cp->b, cp->link);
  1614. X      else if (klist[i] == -1)
  1615. X        printf(" End of chain\n");
  1616. X      else  printf(" illegal klist element\n");
  1617. X  }
  1618. X  for (i = 0; i <= kmax; i++) {
  1619. X      count = -1;
  1620. X      for (cp = (CANDIDATE *)klist[i]; cp > &clist[0]; 
  1621. X        cp = (CANDIDATE *)&cp->link) {
  1622. X        if (++count >= 6) {
  1623. X            printf("\n    ");
  1624. X            count = 0;
  1625. X        }
  1626. X        printf(" (%2d: %2d,%2d -> %d)",
  1627. X            cp-clist, cp->a, cp->b, cp->link);
  1628. X      }
  1629. X      printf("\n");
  1630. X  }
  1631. X  printf("*\n");
  1632. X}
  1633. X#endif
  1634. X#ifdef  TIMING
  1635. Xptime(why)
  1636. Xchar      *why;
  1637. X/*
  1638. X * Dump time buffer
  1639. X */
  1640. X{
  1641. X  long  ttemp;
  1642. X
  1643. X  ttemp = time(NULL);
  1644. X  printf("%ld seconds for %s\n",
  1645. X      ttemp - sectiontime, why);
  1646. X  sectiontime = ttemp;
  1647. X}
  1648. X#endif
  1649. X
  1650. X/*
  1651. X *        s t r e q . c
  1652. X */
  1653. X/*)LIBRARY
  1654. X*/
  1655. X
  1656. X
  1657. X/* #define  EOS  0
  1658. X#define  FALSE  0
  1659. X#define  TRUE  1
  1660. X*/
  1661. Xint
  1662. Xstreq(s1, s2)
  1663. Xregister char  *s1;
  1664. Xregister char  *s2;
  1665. X/*
  1666. X * TRUE if strings are identical
  1667. X */
  1668. X{
  1669. X  while (*s1++ == *s2) {
  1670. X      if (*s2++ == EOS)
  1671. X      return (TRUE);
  1672. X  }
  1673. X  return (FALSE);
  1674. X}
  1675. X/*
  1676. X *        e r r o r . c
  1677. X */
  1678. X
  1679. X/*)LIBRARY
  1680. X*/
  1681. X
  1682. X
  1683. X/* VARARGS */
  1684. Xerror(format, args)
  1685. Xchar      *format;
  1686. X/*
  1687. X * Error message before retiring.
  1688. X */
  1689. X{
  1690. X  fprintf(stderr, format, &args);
  1691. X  putc('\n', stderr);
  1692. X  _error();
  1693. X}
  1694. X
  1695. X_error()
  1696. X{
  1697. X  exit(1);
  1698. X}
  1699. X
  1700. X/* #include  <stdio.h> */
  1701. X  
  1702. Xfputss(s, iop)
  1703. Xregister char *s;
  1704. Xregister FILE *iop;
  1705. X/*
  1706. X * Like fput() except that it puts a newline at the end of the line.
  1707. X */
  1708. X{
  1709. X#ifndef OSK
  1710. X/*
  1711. X * Why wasn't this written like the OSK section?  What's the difference between
  1712. X * fputc and putc other than I've never heard of fputc?
  1713. X */
  1714. X  register c;
  1715. X
  1716. X  while (c = *s++)
  1717. X    fputc(c, iop);
  1718. X  fputc('\n', iop);
  1719. X#else
  1720. X  fputs(s, iop);
  1721. X  putc('\n', iop);
  1722. X#endif
  1723. X}
  1724. X
  1725. X
  1726. X/*
  1727. X * Fgetss() is like fgets() except that the terminating newline
  1728. X * is removed.
  1729. X */
  1730. Xchar *fgetss(s, n, iop)
  1731. Xchar *s;
  1732. Xregister FILE *iop;
  1733. X{
  1734. X  register c;
  1735. X  register char *cs;
  1736. X  
  1737. X  cs = s;
  1738. X  /*
  1739. X   * The getc in the next line used to be an "fgetc".  Change it back if
  1740. X   * getc doesn't work on your system, though that would be odd.
  1741. X   */
  1742. X  while ((c = getc(iop))>=0 && --n>0) {
  1743. X      if (c=='\n')
  1744. X        break;
  1745. X      *cs++ = c;
  1746. X  }
  1747. X  if (c<0 && cs==s)
  1748. X      return((char *)NULL);
  1749. X  *cs = '\0';        /* Overwrite newline as null  */
  1750. X  return(s);
  1751. X}
  1752. SHAR_EOF
  1753. if test 32636 -ne "`wc -c < 'cdiff.c'`"
  1754. then
  1755.     echo shar: error transmitting "'cdiff.c'" '(should have been 32636 characters)'
  1756. fi
  1757. fi
  1758. if test -f 'cdiff.doc'
  1759. then
  1760.     echo shar: will not over-write existing file "'cdiff.doc'"
  1761. else
  1762. echo extracting "'cdiff.doc'"
  1763. sed 's/^X//' >cdiff.doc <<'SHAR_EOF'
  1764. X
  1765. X
  1766. X
  1767. X     CCCCDDDDIIIIFFFFFFFF((((1111))))                    UUUUNNNNIIIIXXXX 5555....0000                     CCCCDDDDIIIIFFFFFFFF((((1111))))
  1768. X
  1769. X
  1770. X
  1771. X     NNNNAAAAMMMMEEEE
  1772. X          cdiff - Public Domain cdiff (context diff) program
  1773. X
  1774. X     SSSSYYYYNNNNOOOOPPPPSSSSIIIISSSS
  1775. X          ddddiiiiffffffff [ ----bbbb ----cccc ----iiii ----eeee ] file1 file2
  1776. X
  1777. X     DDDDEEEESSSSCCCCRRRRIIIIPPPPTTTTIIIIOOOONNNN
  1778. X          _D_i_f_f compares two files, showing what must be changed to
  1779. X          make them identical. Either file1 or file2 (but not both)
  1780. X          may refer to directories. If that is the case, a file in the
  1781. X          directory whose name is the same as the other file argument
  1782. X          will be used. The standard input may be used for one of the
  1783. X          files by replacing the argument by "-". Except for the
  1784. X          standard input, both files must be on disk devices.
  1785. X
  1786. X     OOOOPPPPTTTTIIIIOOOONNNNSSSS
  1787. X          ----bbbb Remove trailing whitespace (blanks and tabs) and compress
  1788. X               all other strings of whitespace to a single blank.
  1789. X
  1790. X          ----cccc Print some context -- matching lines before and after the
  1791. X               non-match section.  Mark non-matched sections with "|".
  1792. X
  1793. X          ----iiii Ignore lower/upper case distinctions.
  1794. X
  1795. X          ----eeee Output is in an "editor script" format which is
  1796. X               compatible with the Unix 'ed' editor.
  1797. X
  1798. X          All information needed to compare the files is maintained in
  1799. X          main memory. This means that very large files (or fairly
  1800. X          large files with many differences) will cause the program to
  1801. X          abort with an "out of space" message. Main memory
  1802. X          requirements (in words) are approximately:
  1803. X
  1804. X                    2 * (length of file1 + length of file2)
  1805. X                           + 3 * (number of changes)
  1806. X
  1807. X          (Where "length" is the number of lines of data in each
  1808. X          file.)
  1809. X
  1810. X          The algorithm reads each file twice, once to build hash
  1811. X          tables and once to check for fortuitous matches (two lines
  1812. X          that are in fact different, but which have the same hash
  1813. X          value). CPU time requirements include sorting the hash
  1814. X          tables and randomly searching memory tables for equivalence
  1815. X          classes. For example, on a time-shared VAX-11/780, comparing
  1816. X          two 1000 line files required about 30 seconds (elapsed clock
  1817. X          time) and about 10,000 bytes of working storage. About 90
  1818. X          per-cent of the time was taken up by file I/O.
  1819. X
  1820. X     DDDDIIIIAAAAGGGGNNNNOOOOSSSSTTTTIIIICCCCSSSS
  1821. X          Warning, bad option 'x'
  1822. X               The option is ignored.
  1823. X
  1824. X
  1825. X
  1826. X     Page 1                                          (printed 1/13/88)
  1827. X
  1828. X
  1829. X
  1830. X
  1831. X
  1832. X
  1833. X     CCCCDDDDIIIIFFFFFFFF((((1111))))                    UUUUNNNNIIIIXXXX 5555....0000                     CCCCDDDDIIIIFFFFFFFF((((1111))))
  1834. X
  1835. X
  1836. X
  1837. X          Usage ...
  1838. X               Two input files were not specified.
  1839. X
  1840. X          Can't open input file "filename".
  1841. X               Can't continue.
  1842. X
  1843. X          Out of space
  1844. X               The program ran out of memory while comparing the two
  1845. X               files.
  1846. X
  1847. X          Can't read line nnn at xxx in file[A/B]
  1848. X               This indicates an I/O error when seeking to the
  1849. X               specific line.  It should not happen.
  1850. X
  1851. X          Spurious match, output is not optimal.
  1852. X               Two lines that were different yielded the same hash
  1853. X               value.  This is harmless except that the difference
  1854. X               output is not the minimum set of differences between
  1855. X               the two files.  For example, instead of the output:
  1856. X                          lines 1 to 5 were changed to ...
  1857. X               the program will print
  1858. X                          lines 1 to 3 were changed to ...
  1859. X                          lines 4 to 5 were changed to ...
  1860. X
  1861. X          The program uses a CRC16 hash code.
  1862. X               The likelihood of this error is quite small.
  1863. X
  1864. X     AAAAUUUUTTTTHHHHOOOORRRR
  1865. X          The diff algorithm was developed by J. W. Hunt and M. D.
  1866. X          McIlroy, using a central algorithm defined by H. S. Stone.
  1867. X          It was published in:
  1868. X               Hunt, J. W., and McIlroy, M. D.,
  1869. X               An Algorithm for Differential File Comparison,
  1870. X               Computing Science Technical Report #41,
  1871. X               Bell Laboratories, Murray Hill, NJ  07974
  1872. X
  1873. X     BBBBUUUUGGGGSSSS
  1874. X          On RSX and DECUS C on VMS systems, diff may fail if the both
  1875. X          files are not "variable-length, implied carriage control"
  1876. X          format.  The scopy program can be used to convert files to
  1877. X          this format if problems arise.
  1878. X
  1879. X          When compiled under VAX C, diff handles STREAM_LF files
  1880. X          properly (in addition to the canonical variable-length
  1881. X          implied carriage control files). Other variations should
  1882. X          work, but have not been tested.
  1883. X
  1884. X          When compiled under VAX C, diff is quite slow for unknown
  1885. X          reasons which ought to be investigated. On the other hand,
  1886. X          it has access to effectively unlimited memory.
  1887. X
  1888. X          Output in a form suitable for ed - the -e option - seems
  1889. X
  1890. X
  1891. X
  1892. X     Page 2                                          (printed 1/13/88)
  1893. X
  1894. X
  1895. X
  1896. X
  1897. X
  1898. X
  1899. X     CCCCDDDDIIIIFFFFFFFF((((1111))))                    UUUUNNNNIIIIXXXX 5555....0000                     CCCCDDDDIIIIFFFFFFFF((((1111))))
  1900. X
  1901. X
  1902. X
  1903. X          rather pointless; the analogue on DEC systems is SLP (SUMSLP
  1904. X          on VMS). It would be simple to provide SLP-compatible
  1905. X          output. The question is, why bother - since the various DEC
  1906. X          file comparison utilities already produce it.
  1907. X
  1908. X
  1909. X
  1910. X
  1911. X
  1912. X
  1913. X
  1914. X
  1915. X
  1916. X
  1917. X
  1918. X
  1919. X
  1920. X
  1921. X
  1922. X
  1923. X
  1924. X
  1925. X
  1926. X
  1927. X
  1928. X
  1929. X
  1930. X
  1931. X
  1932. X
  1933. X
  1934. X
  1935. X
  1936. X
  1937. X
  1938. X
  1939. X
  1940. X
  1941. X
  1942. X
  1943. X
  1944. X
  1945. X
  1946. X
  1947. X
  1948. X
  1949. X
  1950. X
  1951. X
  1952. X
  1953. X
  1954. X
  1955. X
  1956. X
  1957. X
  1958. X     Page 3                                          (printed 1/13/88)
  1959. X
  1960. X
  1961. X
  1962. SHAR_EOF
  1963. if test 6151 -ne "`wc -c < 'cdiff.doc'`"
  1964. then
  1965.     echo shar: error transmitting "'cdiff.doc'" '(should have been 6151 characters)'
  1966. fi
  1967. fi
  1968. if test -f 'cdiff.mem'
  1969. then
  1970.     echo shar: will not over-write existing file "'cdiff.mem'"
  1971. else
  1972. echo extracting "'cdiff.mem'"
  1973. sed 's/^X//' >cdiff.mem <<'SHAR_EOF'
  1974. X
  1975. X
  1976. XJan 13 13:00 1988  CDIFF.MEM Page 1
  1977. X
  1978. X
  1979. X
  1980. X     Diff maintains all information needed to compare the two files in
  1981. X     main memory. This means that very large files (or fairly large
  1982. X     files with many differences) will cause the program to abort with
  1983. X     an "out of space" error. Main memory requirements (in words) are
  1984. X     approximately:
  1985. X
  1986. X      2 * (length of file1 + length of file2) + (3 * number of changes)
  1987. X
  1988. X     The diff algorithm reads each file twice (once to build hash
  1989. X     tables and a second time to check for fortuitous matches), then
  1990. X     reads the differences by seeking randomly within the files. CPU
  1991. X     time requirements include sorting the two hash vectors and
  1992. X     randomly searching memory tables for equivalence classes. For
  1993. X     example, running in Vax compatibility mode, two 1000 line files
  1994. X     with a fair number of differences took about 25 seconds (elapsed
  1995. X     wall clock time) for processing. Most of this time was spent in
  1996. X     the file read routines. This test required slightly more than
  1997. X     6000 words of memory for internal tables.
  1998. X
  1999. X     The diff algorithm was developed by J. W. Hunt and M. D. McIlroy,
  2000. X     using a central algorithm defined by H. S. Stone. The algorithm
  2001. X     was described in:
  2002. X
  2003. X          Hunt, J. W., and McIlroy, M. D.,
  2004. X          An Algorithm for Differential File Comparison,
  2005. X          Computing Science Technical Report #41,
  2006. X          Bell Laboratories, Murray Hill, NJ  07974
  2007. X
  2008. X     The following description is summarized from that document. While
  2009. X     it has been slightly modified to correspond to the program
  2010. X     source, the algorithm is essentially identical.
  2011. X
  2012. X     1. Read the input files, building two vectors containing the line
  2013. X     number (serial) and hash value (hash) of each line. Data for
  2014. X     fileA will be in a vector pointed to by fileA[], while data for
  2015. X     fileB will be pointed to by fileB[]. The lengths (number of
  2016. X     lines) of the files will be represented by lenA and lenB
  2017. X     respectively. [This is slightly different from the published
  2018. X     algorithm.]
  2019. X
  2020. X     2. Note initial and final sequences that have identical hash
  2021. X     values to shorten subsequent processing. Note that the "jackpot"
  2022. X     phase (step 9.) will examine all lines in the file. Next, sort
  2023. X     each file using hash as the primary key and serial as the
  2024. X     secondary key.
  2025. X
  2026. X     3. Construct an array of equivalence classes (member[1..lenB])
  2027. X     where each element contains the line number in fileB and a flag
  2028. X     which is True if the indicated line is the first member of an
  2029. X     equivalence class. (An equivalence class is a set of lines which
  2030. X     all hash to the same value. The lines themselves are not
  2031. X     necessarily identical.)
  2032. X
  2033. X     4. Construct an array, class[1..lenA], where each element, I, is
  2034. X     set to the index of a line, J, in fileB if member[J] is the first
  2035. X
  2036. X
  2037. X
  2038. X
  2039. X
  2040. X
  2041. X
  2042. XJan 13 13:00 1988  CDIFF.MEM Page 2
  2043. X
  2044. X
  2045. X     element in an equivalence class and the hash code of line[I] in
  2046. X     fileA is the same as the hash code of line[J] in fileB. Class[I]
  2047. X     is set to zero if no such line exists.
  2048. X
  2049. X     If non-zero, class[I] now points in member[] to the beginning of
  2050. X     the class of lines in fileB equivalent to line[I] in fileA.
  2051. X
  2052. X     The next two steps implement the longest common subsequence
  2053. X     algorithm.
  2054. X
  2055. X     5. Structure CANDIDATE { a, b, previous }, where a and b are line
  2056. X     numbers and previous a reference to a candidate, will store
  2057. X     candidate lists as they are constructed.
  2058. X
  2059. X     Vector clist[] stores references to candidates. It is dimensioned
  2060. X     (0..min(lenA, lenB) + 1)
  2061. X
  2062. X      Initialize
  2063. X          clist[0] = CANDIDATE {   0,   0, -1 };
  2064. X          clist[1] = CANDIDATE { A+1, B+1, -1 };
  2065. X          ktop = 1;
  2066. X
  2067. X     clist[1] is a fence beyond the last usefully filled element and
  2068. X     -1 is an out-of-range clist index. Ktop is the index of the
  2069. X     fence. Note, because of the memory allocation used, clist[] is
  2070. X     actually composed of two vectors, clist[] containing the
  2071. X     candidate reference, and klist[] containing pointers to clist.
  2072. X
  2073. X     6.  For (A = 1 to lenA) {
  2074. X          I = class[A];  -- the index in member[]:  beginning of
  2075. X                -- the class of lines in fileB equivalent
  2076. X                -- to this line in fileA.
  2077. X          if (I is non-zero) {
  2078. X            Merge each member into the candidate list
  2079. X            as discussed below.
  2080. X          }
  2081. X
  2082. X     Unravel the chain of candidates, getting a vector of common
  2083. X     subsequences:
  2084. X
  2085. X     7.  Set all elements of match[0..lenA] to zero.
  2086. X
  2087. X     8. clist[ktop-1] points to the subsequence chain head. For each
  2088. X     element in the chain, let A and B be the line number entries.
  2089. X     Then, set
  2090. X
  2091. X          match[A] = B;
  2092. X
  2093. X     The non-zero elements of match[] now pick out a longest common
  2094. X     subsequence chain, possibly including spurious matches due to
  2095. X     hash coincidences. The pairings between the two files are:
  2096. X
  2097. X      if (match[A] is non-zero) {
  2098. X          line A in fileA matches line match[A] in fileB;
  2099. X      }
  2100. X
  2101. X
  2102. X
  2103. X
  2104. X
  2105. X
  2106. X
  2107. X
  2108. XJan 13 13:00 1988  CDIFF.MEM Page 3
  2109. X
  2110. X
  2111. X     Now, read each line of fileA and fileB to check for jackpots:
  2112. X
  2113. X     9.  for (A = 1 to lenA) {
  2114. X          if (match[A] is nonzero) {
  2115. X            if (fileA[A] is not identical to fileB[match[A]])
  2116. X                match[A] = 0;  -- Hash congruence
  2117. X          }
  2118. X      }
  2119. X
  2120. X     Ignoring "squish" complications, the merge step may be defined as
  2121. X     follows:
  2122. X
  2123. X      Entry:
  2124. X          clist[]      Candidate pointer array
  2125. X          ktop      Fence beyond last filled index
  2126. X          A      Current index in fileA
  2127. X          member[]  Equivalence vector
  2128. X          I      The index in member[] of the first element
  2129. X                  of the class of lines in fileB that are
  2130. X                  equivalent to line[A] in fileA.
  2131. X
  2132. X     1. Let clist[R] be "an r-candidate" and C a reference to the last
  2133. X     candidate found, which will always be an r-candidate. clist[R]
  2134. X     will be updated with this reference once the previous value of
  2135. X     clist[R] is no longer needed. Initialize:
  2136. X
  2137. X         R = 0; C = clist[0];
  2138. X
  2139. X     2.  Do steps 3 through 6 repeatedly:
  2140. X
  2141. X     3. set B to the line number in member[I]; search clist[R..ktop]
  2142. X     for an element, clist[S], such that
  2143. X
  2144. X          clist[S-1].b < B and clist[S].b >= B
  2145. X
  2146. X     Note that clist[] is ordered on clist[].b so that binary search
  2147. X     will work. The search algorithm used requires the two "fence"
  2148. X     entries described above.
  2149. X
  2150. X     If such an element is found, perform steps 4. and 5.
  2151. X
  2152. X     4. Extend the longest common subsequence chain:
  2153. X
  2154. X          If (clist[S].b > B) {
  2155. X            clist[R] = C;
  2156. X            R = S;
  2157. X            C = candidate(A, B, clist[S - 1]);
  2158. X          }
  2159. X
  2160. X     5. Extend the number of subsequences, moving the fence:
  2161. X
  2162. X          If (S == ktop) {
  2163. X            clist[ktop + 1] = clist[ktop]
  2164. X            ktop = ktop + 1;
  2165. X            break out of step 2's loop;
  2166. X          }
  2167. X
  2168. X
  2169. X
  2170. X
  2171. X
  2172. X
  2173. X
  2174. XJan 13 13:00 1988  CDIFF.MEM Page 4
  2175. X
  2176. X
  2177. X
  2178. X      6.  I = I + 1;
  2179. X          if (member[I] is the first element in another class) {
  2180. X              break out of step 2's loop;
  2181. X           }
  2182. X          else {
  2183. X              continue at step 2.
  2184. X           }
  2185. X
  2186. X     7. clist[R] = C; exit merge subroutine.
  2187. X
  2188. X     To illustrate vector contents, here is a sample run:
  2189. X
  2190. X     File A:
  2191. X      line 1
  2192. X      line 2
  2193. X      line 3
  2194. X      line 4
  2195. X      line 5 gets deleted
  2196. X      line 6
  2197. X
  2198. X     File B:
  2199. X      line 1
  2200. X      line 1.5 inserted
  2201. X      line 2
  2202. X      line 3 changed
  2203. X      line 4
  2204. X      line 6
  2205. X
  2206. X     (For clarity, the "squish" step is omitted from the following)
  2207. X
  2208. X     On entry to equiv() (after readin and sorting), the file[] vector
  2209. X     is as follows (the first entry in each pair is the line number,
  2210. X     the second is the hash value. Entries are sorted on hash value):
  2211. X
  2212. X     FileA[] (1..lines in fileA):
  2213. X       line   hash
  2214. X      3 042400  6 043300  5 050026  1 102201  2 102701  4 103501
  2215. X     FileB[] (1..lines in fileB):
  2216. X      6 043300  2 045600  1 102201  3 102701  5 103501  4 147166
  2217. X
  2218. X
  2219. X     After Equiv has processed file[]:
  2220. X
  2221. X     FileA[] (1..lines in fileA):
  2222. X       line value
  2223. X      3 0  6 1  5 0  1 3  2 4  4 5
  2224. X     Member[] (0..lines in fileB)
  2225. X      0  -6  -2  -1  -3  -5  -4
  2226. X
  2227. X     After unsort() has unwound fileB:
  2228. X
  2229. X     Class[] (1 .. lines in fileA):
  2230. X      3   4  0  5  0  1
  2231. X
  2232. X     Within unravel(), match is built in the following order:
  2233. X
  2234. X
  2235. X
  2236. X
  2237. X
  2238. X
  2239. X
  2240. XJan 13 13:00 1988  CDIFF.MEM Page 5
  2241. X
  2242. X
  2243. X
  2244. X      match[6] := 6
  2245. X      match[4] := 5
  2246. X      match[2] := 3
  2247. X      match[1] := 1
  2248. X
  2249. X     Match[] (0 .. lines in fileA):
  2250. X
  2251. X       0  1  3  0  5  0  6
  2252. X
  2253. X     Output is as follows:
  2254. X
  2255. X      1a2
  2256. X      > line 1.5 inserted
  2257. X      3c4
  2258. X      < line 3
  2259. X      ---
  2260. X      > line 3 changed
  2261. X      5d5
  2262. X      < line 5 gets deleted
  2263. X
  2264. X********************************************************************
  2265. X
  2266. X/*
  2267. X *        s t r e q . c
  2268. X */
  2269. X
  2270. X
  2271. XString Equality Test
  2272. XString equality test
  2273. X
  2274. XSynopsis:
  2275. X  streq(a, b);
  2276. X  char      *a;
  2277. X  char      *b;
  2278. X
  2279. XDescription:
  2280. X
  2281. X  Return TRUE if the strings are equal.
  2282. X
  2283. XBugs
  2284. X
  2285. X***************************************************************
  2286. X
  2287. X/*
  2288. X *        e r r o r . c
  2289. X */
  2290. X
  2291. X
  2292. XFatal Error Exit
  2293. X
  2294. X    Synopsis:
  2295. X
  2296. X      _error()
  2297. X
  2298. X      error(format, args)
  2299. X
  2300. X
  2301. X
  2302. X
  2303. X
  2304. X
  2305. X
  2306. XJan 13 13:00 1988  CDIFF.MEM Page 6
  2307. X
  2308. X
  2309. X      char      *format;
  2310. X
  2311. X    Documentation:
  2312. X
  2313. X      Fatal error exits.  _error() halts, error() prints something
  2314. X      on stderr and then halts.
  2315. X
  2316. X    Bugs:
  2317. X
  2318. X      THIS DOES NOT WORK ON MANY SYSTEMS DUE TO EXTREMLY NON-PORTABLE CODE.
  2319. X      Why oh why can't people learn to use varargs properly?  This code will
  2320. X      blow up on OSK.  Fortunatly, it isn't used often...
  2321. X
  2322. X
  2323. X
  2324. X
  2325. X
  2326. X
  2327. X
  2328. X
  2329. X
  2330. X
  2331. X
  2332. X
  2333. X
  2334. X
  2335. X
  2336. X
  2337. X
  2338. X
  2339. X
  2340. X
  2341. X
  2342. X
  2343. X
  2344. X
  2345. X
  2346. X
  2347. X
  2348. X
  2349. X
  2350. X
  2351. X
  2352. X
  2353. X
  2354. X
  2355. X
  2356. X
  2357. X
  2358. X
  2359. X
  2360. X
  2361. X
  2362. X
  2363. X
  2364. X
  2365. X
  2366. X
  2367. X
  2368. X
  2369. X
  2370. SHAR_EOF
  2371. if test 9327 -ne "`wc -c < 'cdiff.mem'`"
  2372. then
  2373.     echo shar: error transmitting "'cdiff.mem'" '(should have been 9327 characters)'
  2374. fi
  2375. fi
  2376. # end of shell archive
  2377. exit 0
  2378.  
  2379.